#pragma once

#include <google/protobuf/service.h>
#include <mutex>
#include <memory>
#include "../Timestamp.h"
#include "../Atomic.h"

class Buffer;
class TcpConnection;
typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;
typedef std::shared_ptr<google::protobuf::Message> MessagePtr;

class RpcMessage;
typedef std::shared_ptr<RpcMessage> RpcMessagePtr;

class RpcChannel : public ::google::protobuf::RpcChannel
{
public:
    RpcChannel();
    explicit RpcChannel(const TcpConnectionPtr& conn);
    ~RpcChannel();

    void setConnection(const TcpConnectionPtr& conn) { conn_ = conn; }
    void setServices(const std::map<std::string, ::google::protobuf::Service*>* services)
    { services_ = services; }

    void CallMethod(const ::google::protobuf::MethodDescriptor* method,
                  ::google::protobuf::RpcController* controller,
                  const ::google::protobuf::Message* request,
                  ::google::protobuf::Message* response,
                  ::google::protobuf::Closure* done) override;

    void onMessage(const TcpConnectionPtr& conn,
                    Buffer* buf);

private:
    // void onRpcMessage( );
    void onRpcMessage(const TcpConnectionPtr& conn,
                            const RpcMessagePtr& messagePtr);

    void doneCallback(::google::protobuf::Message* response, int64_t id);

    struct OutstandingCall
    {
        ::google::protobuf::Message* response;
        ::google::protobuf::Closure* done;
    };
    
    // const ::google::protobuf::Message* prototype_;
    const RpcMessagePtr prototype_;
    TcpConnectionPtr conn_;
    AtomicInt64 id_;

    std::mutex mutex_;
    std::map<int64_t, OutstandingCall> outstandings_;

    const std::map<std::string, ::google::protobuf::Service*>* services_;


};

typedef std::shared_ptr<RpcChannel> RpcChannelPtr;

void sendRpcMessage(const TcpConnectionPtr& conn, const ::google::protobuf::Message& message);
bool parseMessage(const TcpConnectionPtr& conn, Buffer* buf, std::string& msg);
